// phalanx define_func.hpp.cu header file

// Copyright (c) 2011 - 2011 Kohei Takahashi (Flast).
// Distributed under the MIT license. for more detail see COPYING.

#ifndef IG_PHALANX_DETAIL_DEFINE_FUNC_HPP_CU_ONCE_
#define IG_PHALANX_DETAIL_DEFINE_FUNC_HPP_CU_ONCE_

#include <phalanx/config.hpp.cu>

#include <boost/preprocessor/inc.hpp>
#include <boost/preprocessor/repeat.hpp>
#include <boost/preprocessor/comma_if.hpp>
#include <boost/preprocessor/comparison/not_equal.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
#include <boost/preprocessor/seq/for_each.hpp>

// utility
#define LO_PHALANX_PP_EXPAND_ARGS_( unused_z_, num_, params_ ) \
  BOOST_PP_TUPLE_ELEM( \
    BOOST_PP_TUPLE_ELEM( 3, 0, params_ ), \
    num_, \
    BOOST_PP_TUPLE_ELEM( 3, 1, params_ ) ) \
  BOOST_PP_TUPLE_ELEM( \
    BOOST_PP_TUPLE_ELEM( 3, 0, params_ ), \
    num_, \
    BOOST_PP_TUPLE_ELEM( 3, 2, params_ ) ) \
  BOOST_PP_COMMA_IF( BOOST_PP_NOT_EQUAL( \
    BOOST_PP_TUPLE_ELEM( 3, 0, params_ ), \
    BOOST_PP_INC( num_ ) ) )

#define LO_PHALANX_PP_PLACEHOLDER_( unused_z_, num_, end_ ) \
  placeholder_ ## num_ ## _ \
  BOOST_PP_COMMA_IF( BOOST_PP_NOT_EQUAL( end_, BOOST_PP_INC( num_ ) ) )

#define LO_PHALANX_PP_GEN_UNNAMED_ARGS_( num_ ) \
  ( BOOST_PP_REPEAT( \
    num_, \
    LO_PHALANX_PP_PLACEHOLDER_, \
    num_ ) )

#define LO_PHALANX_PP_LISTUP_UNNAMED_PARAMS_( num_, types_ ) \
  BOOST_PP_REPEAT( \
    num_, \
    LO_PHALANX_PP_EXPAND_ARGS_, \
    ( num_, types_, LO_PHALANX_PP_GEN_UNNAMED_ARGS_( num_ ) ) )

// Overloads
#define LO_PHALANX_PP_DEF_OVERLOAD( hostable_ ) \
  LO_PHALANX_PP_DEF_OVERLOAD_ ## hostable_

#define LO_PHALANX_PP_DEF_OVERLOAD_true( name_, count_, ret_, argt_, argv_, body_ ) \
  LO_PHALANX_PP_DEF_OVERLOAD_DETAIL_( true, name_, count_, ret_, argt_, argv_, body_ )
#define LO_PHALANX_PP_DEF_OVERLOAD_false( name_, count_, ret_, argt_, argv_, body_ ) \
  LO_PHALANX_PP_DEF_OVERLOAD_DETAIL_( false, name_, count_, ret_, argt_, argv_, body_ )

#define LO_PHALANX_PP_DEF_OVERLOAD_DETAIL_( hostable_, name_, count_, ret_, argt_, argv_, body_ ) \
  BOOST_PP_REPEAT( \
    BOOST_PP_TUPLE_ELEM( 2, 0, count_ ), \
    LO_PHALANX_PP_EXPAND_DEF_, \
    ( hostable_, name_, count_, ret_, argt_, argv_, body_ ) )

#define LO_PHALANX_PP_EXPAND_DEF_( z_, num_, params_ ) \
  LO_PHALANX_PP_DEF_OVERLOAD_IMPL_( \
    BOOST_PP_TUPLE_ELEM( 7, 0, params_ ), \
    z_, \
    BOOST_PP_TUPLE_ELEM( 7, 1, params_ ), \
    BOOST_PP_TUPLE_ELEM( 2, 1, BOOST_PP_TUPLE_ELEM( 7, 2, params_ ) ), \
    BOOST_PP_TUPLE_ELEM( \
      BOOST_PP_TUPLE_ELEM( 2, 0, BOOST_PP_TUPLE_ELEM( 7, 2, params_ ) ), \
      num_, \
      BOOST_PP_TUPLE_ELEM( 7, 3, params_ ) ), \
    BOOST_PP_TUPLE_ELEM( \
      BOOST_PP_TUPLE_ELEM( 2, 0, BOOST_PP_TUPLE_ELEM( 7, 2, params_ ) ), \
      num_, \
      BOOST_PP_TUPLE_ELEM( 7, 4, params_ ) ), \
    BOOST_PP_TUPLE_ELEM( 7, 5, params_ ), \
    BOOST_PP_TUPLE_ELEM( 7, 6, params_ ) )

#define LO_PHALANX_PP_DEF_OVERLOAD_IMPL_( hostable_, z_, name_, argc_, return_, types_, args_, decl_ ) \
  PHALANX_CONFIG_DEFAULT_FUNCTION( hostable_ ) \
  return_ \
  name_( \
    BOOST_PP_REPEAT_ ## z_( \
      argc_, \
      LO_PHALANX_PP_EXPAND_ARGS_, \
      ( argc_, types_, args_ ) ) ) \
  decl_


// Generic
// XXX: seq_ would be contain tuple form; like a ( int, ( int ), f ).
//      The form is ill-formed iff contain not unary template specialization.
//  examples)
//      LO_PHALANX_PP_GENERIC( false )(
//          example, 1,
//          (( array< int >, ( int ), example_aI_I )) ); // OK
//
//      LO_PHALANX_PP_GENERIC( true )(
//          example, 1,
//          (( pair< int, int >, ( int ), example_pII_I )) ); // error
//      LO_PHALANX_PP_GENERIC( true )(
//          example, 1,
//          (( int, ( pair< int, int > ), example_I_pII )) ); // error
#define LO_PHALANX_PP_GENERIC( hostable_ ) \
  LO_PHALANX_PP_GENERIC_DETAIL_ ## hostable_

#define LO_PHALANX_PP_GENERIC_DETAIL_true( name_, argc_, seq_ ) \
  LO_PHALANX_PP_GENERIC_DETAIL( name_, argc_, seq_, true )
#define LO_PHALANX_PP_GENERIC_DETAIL_false( name_, argc_, seq_ ) \
  LO_PHALANX_PP_GENERIC_DETAIL( name_, argc_, seq_, false )

#define LO_PHALANX_PP_GENERIC_DETAIL( name_, argc_, seq_, hostable_ ) \
  BOOST_PP_SEQ_FOR_EACH( \
    LO_PHALANX_PP_EXPAND_REDEF_BODY_, \
    ( name_, argc_, hostable_ ), \
    seq_ )

#define LO_PHALANX_PP_EXPAND_REDEF_BODY_( unused_r_, data_, info_ ) \
  PHALANX_CONFIG_DEFAULT_FUNCTION( BOOST_PP_TUPLE_ELEM( 3, 2, data_ ) ) \
  BOOST_PP_TUPLE_ELEM( 3, 0, info_ ) \
  BOOST_PP_TUPLE_ELEM( 3, 0, data_ )( \
    LO_PHALANX_PP_LISTUP_UNNAMED_PARAMS_( \
      BOOST_PP_TUPLE_ELEM( 3, 1, data_ ), \
      BOOST_PP_TUPLE_ELEM( 3, 1, info_ ) ) ) \
  { return BOOST_PP_TUPLE_ELEM( 3, 2, info_ ) \
      LO_PHALANX_PP_GEN_UNNAMED_ARGS_( \
        BOOST_PP_TUPLE_ELEM( 3, 1, data_ ) ); }

#endif // IG_PHALANX_DETAIL_DEFINE_FUNC_HPP_CU_ONCE_

